﻿#region

using System;
using System.Collections.Generic;
using System.Linq;

#endregion

namespace Domain {
    public class Graph<TEdge> where TEdge : IEdge, new() {
        public int NumberVertices {
            get { return Vertices.Count; }
        }

        public List<TEdge> Edges = new List<TEdge>();
        public readonly List<Vertex> Vertices = new List<Vertex>();

        public bool IsEmpty {
            get { return Edges.Count == 0 && Vertices.Count == 0; }
        }

        public int NumberEdges {
            get { return Edges.Count; }
        }

        public Graph(List<Vertex> vertices) {
            Vertices = vertices;
        }

        public Graph(int numberVertices) {
            var vertices = new Vertex[numberVertices];
            for (var i = 0; i < numberVertices; i++) {
                vertices[i] = new Vertex(i);
            }
            Vertices = vertices.ToList();
        }

        public Graph(Graph<TEdge> graph) {
            Dictionary<Vertex, Vertex> d;
            CopyGraph(graph, out d);
        }

        public Graph(Graph<TEdge> graph, out Dictionary<Vertex, Vertex> d) {
            CopyGraph(graph, out d);
        }

        private void CopyGraph(Graph<TEdge> graph, out Dictionary<Vertex, Vertex> d) {
            var vertexDictionary = new Dictionary<Vertex, Vertex>();

            foreach (var vertex in graph.Vertices) {
                var newVertex = new Vertex(vertex);
                vertexDictionary.Add(vertex, newVertex);
                Vertices.Add(newVertex);
            }
            foreach (var edge in graph.Edges) {
                var vertices = edge.Vertices.Select(vertex => vertexDictionary[vertex]).ToList();
                var newEdge = new TEdge(){Vertices = vertices};
                Edges.Add(newEdge);
            }

            d = vertexDictionary;
        }

        public Graph<TEdge> AddEdge(TEdge e) {
            Edges.Add(e);
            return this;
        }

        public void Remove(Vertex vertex) {
            if (!Vertices.Contains(vertex)) return;
            Vertices.Remove(vertex);
            var edges = Edges.Where(edge => edge.Vertices.Contains(vertex)).ToList();
            for (int i = 0; i < edges.Count; i++) {
                var edge = edges[i];
                edge.Vertices.Remove(vertex);
                if (edge.Vertices.Count ==0) {
                    Edges.Remove(edge);
                }
            }
        }

        public void Remove(TEdge edge) {
            if (!Edges.Contains(edge)) return;
            Edges.Remove(edge);
        }

        public bool IsMAcyclic {
            get {
                var graph = this as Graph<HyperEdge>;
                if (graph == null) {
                    throw new ApplicationException(
                        "Свойство М-ацикличности может быть определено только для гиперграфов");
                }

                var reducedGraph = Graham.BuildReducedGraph(graph);
                return reducedGraph.IsEmpty;
            }
        }
    }
}